package cxm.proj.xtalk.smack;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import cxm.lib.util.MyLog;
import cxm.lib.util.NetworkHelper;

public class UdpCall extends Call {
	private static final String Spliter = " ";
	private DatagramSocket serverSocket = null;
	private DatagramPacket packet = null;
	private DatagramSocket clientSocket = null;
	private DatagramPacket sendPacket = null;
	private int selfPort = 0;
	private int readDataLen = 0;
	private String targetAddr = "";
	private int targetPort = 0;
	private int targetDataLen = 0;
	private byte []recvBuffer = null;
	// private NotifyMan notifyMan = null;
	
	public UdpCall(String caller, String callee, int port, int dataLen) {
		super(caller, callee);
		this.selfPort = port;
		this.readDataLen = dataLen;
		this.recvBuffer = new byte[this.readDataLen];
		// this.notifyMan = new NotifyMan(dataLen);
	}
	
	@Override
	public String getSelfIdentical() {
		String ipAddr = NetworkHelper.getLocalIpAddress();
		if (null == ipAddr)
			throw new RuntimeException("Cannot get ip address");
		String id = ipAddr + Spliter + this.selfPort + Spliter + this.readDataLen;
		
		return id;
	}
	
	@Override
	public void setTargetIdentical(String targetId) {
		String []res = targetId.split(Spliter, 3);
		
		this.targetAddr = res[0];
		this.targetPort = Integer.parseInt(res[1]);
		this.targetDataLen = Integer.parseInt(res[2]);
	}

	@Override
	protected boolean onBeforeRun() {
		// set thread priority
		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
		Thread.currentThread().setName("UdpCall");

		try {
			// this.notifyMan.startNotifyThread();

			this.clientSocket = new DatagramSocket();
			this.serverSocket = new DatagramSocket(this.selfPort);
			this.serverSocket.setSoTimeout(500); // 500ms
			packet = new DatagramPacket(this.recvBuffer, this.recvBuffer.length);
		} catch(Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot create socket: " + e);
			return false;
		}
		return true;
	}
	
	@Override
	protected void onAfterRun() {
		this.serverSocket.close();
		this.clientSocket.close();
		this.serverSocket = null;
		this.clientSocket = null;
	}

	// private long totalSum = 0;
	// private int count = 0;
	@Override
	protected void onRunOnce() {
		// count++;
		// long startTime = System.currentTimeMillis();

		try {
			this.serverSocket.receive(this.packet);
		} catch (Exception e) {
			MyLog.Log(MyLog.Level.Error, "Cannot receive packet: " + e);
			return;
		}
		if (this.packet.getLength() != this.readDataLen) {
			MyLog.Log(MyLog.Level.Error, "Package size mismatch");
			return;
		}
		
		this.fireOnDataReceived(this.packet.getData());
		// this.notifyMan.AddBuffer(this.packet.getData());

		/*
		this.totalSum += (System.currentTimeMillis() - startTime);
		if (count % 100 == 0)
			MyLog.Log(MyLog.Level.Debug, String.format("Receive package %d with time %dms",
					count, totalSum));
		*/
		
	}

	
	@Override
	protected void onBeforeStop() {
		this.serverSocket.close();
		// this.notifyMan.stopNotifyThread();
	}
	
	public void sendData(byte []data) throws Exception {
		if (data.length != this.targetDataLen)
			throw new Exception("Data len mismatch");
		
		if (null == this.sendPacket) {
			// reuse udp socket
			InetAddress IPAddress = InetAddress.getByName(this.targetAddr);
			this.sendPacket = new DatagramPacket(
					data, data.length, IPAddress, this.targetPort);
		} else {
			this.sendPacket.setData(data);
		}
		this.clientSocket.send(sendPacket);
	}
	
	/*
	// To notify data received by socked
	public class NotifyMan implements Runnable {
		private Thread thread;
		private boolean isRun = false;
		RingBuffer ringBuffer = new RingBuffer(1024 * 1024);
		private byte[] notifyBuffer = null;
		
		public NotifyMan(int dataLen) {
			this.notifyBuffer = new byte[dataLen];
		}

		public void AddBuffer(byte []buffer) {
			this.ringBuffer.putBuffer(buffer);
		}

		public void startNotifyThread() {
			this.thread = new Thread(this);
			this.isRun = true;
			this.thread.start();
		}

		public void stopNotifyThread() {
			try {
				this.isRun = false;
				this.thread.join();
			} catch (Exception e) {
				MyLog.Log(MyLog.Level.Error, "Cannot join thread: " + e);
			}
		}

		@Override
		public void run() {
			// Thread.currentThread().setName("NotifyMan");
			while (this.isRun) {
				int len = this.ringBuffer.getBuffer(this.notifyBuffer);
				if (len == this.notifyBuffer.length)
					UdpCall.this.fireOnDataReceived(this.notifyBuffer);
			}
		}
	}
	*/
}
